/*
 * @Author: wangke
 * @Date: 2025-07-28 23:57:01
 * @LastEditors: wangke
 * @LastEditTime: 2025-07-31 23:42:28
 * 
 */
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "Common.h"
#include "App_Sd.h"
#include "App_I2S.h"
#include <base64.h> 
#include "App_Screen.h"


// Baidu API credentials
const char* baidu_api_key = "BMMKA5Y5UlflSKk28t16dV4T";
const char* baidu_secret_key = "C52DWv4AayWtCGhguKjd1CPtRlZu7NXa";


// 百度 AppBuilder Agent api ke y
const char* appbuilder_api_key = "bce-v3/ALTAK-7X3NEdufsTRw2pyI0LCUm/136906b4fe0f5b877ab8168c1e30aafdafc9753a";
const char* appbuilder_app_id = "7e8d70fb-248f-4785-b558-6dea8392447d";
String appbuilder_conversation_id = "";

// 音色配置
#define PERSON_VOICE 5  //(1：度小宇， 0：度小美， 3：度逍遥， 4：度丫丫，5118：度小鹿，106：度博文，110：度小童，111：度小萌，103：度米朵，5：度小娇)



//把从百度获取到的access_token写入到SD卡，access_token每隔一个月会失效，每隔一个月需要获取一次
void saveAccessToken(const String& token) {
  writeFile(SD, "/Access_token.txt", token.c_str());
  Serial0.println("access_token 写入成功");
}


String   readFileToString(fs::FS &fs, const char * path) {
    File file = fs.open(path);
    if (!file) {
        Serial.println("Failed to open file for reading");
        return "";
    }

    String content = "";
    while (file.available()) {
        content += (char)file.read();  // 累加到字符串中
    }
    file.close();
    return content;
}





// Get Baidu API access token ,获取百度api访问令牌，并写入SD卡，
String getAccessToken(const char* api_key, const char* secret_key) {
  String access_token = "";
  String URL = "";
  HTTPClient http;

  // 创建http请求
  URL = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=" + String(api_key) + "&client_secret=" + String(secret_key);
  http.begin(URL);
  int httpCode = http.POST("");

  if (httpCode == HTTP_CODE_OK) {
    String response = http.getString();
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, response);
    access_token = doc["access_token"].as<String>();

    printfme("[HTTP] GET access_token: %s\n", access_token.c_str());
    saveAccessToken(access_token);   //将获取到的令牌写入SD卡
  } else {
    printfme("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  http.end();

  return access_token;
}



//往百度服务器传音频然后返回识别后的文字
String baiduSTT_Send(String access_token, uint8_t* audioData, int audioDataSize) {
  String recognizedText = "";

  if (access_token == "") {
    Serial0.println("access_token is null");
    return recognizedText;
  }

  // Base64 encode audio data
  String base64Audio = base64::encode(audioData, audioDataSize);

  // 拼接 JSON
  String data_json = "{";
  data_json += "\"format\":\"pcm\",";
  data_json += "\"rate\":16000,";
  data_json += "\"dev_pid\":1537,";
  data_json += "\"channel\":1,";
  data_json += "\"cuid\":\"test123\",";
  data_json += "\"token\":\"" + access_token + "\",";
  data_json += "\"len\":" + String(audioDataSize) + ",";
  data_json += "\"speech\":\"" + base64Audio + "\"";
  data_json += "}";


  // 创建http请求
  HTTPClient http_client;

  http_client.begin("http://vop.baidu.com/server_api");
  http_client.addHeader("Content-Type", "application/json");
  int httpCode = http_client.POST(data_json);

  if (httpCode > 0) {
    if (httpCode == HTTP_CODE_OK) {
      // 获取返回结果
      String response = http_client.getString();     

      // 从json中解析对应的result
      DynamicJsonDocument responseDoc(2048);
      deserializeJson(responseDoc, response);
      recognizedText = responseDoc["result"].as<String>();
    }
  } else {
    Serial.printf("[HTTP] POST failed, error: %s\n", http_client.errorToString(httpCode).c_str());
  }

  http_client.end();

  return recognizedText;
}



// 获取对话ID
String getConversation_id(const char* api_key, const char* app_id) {
  String conversation_id = "";

  // 创建http请求
  HTTPClient http;
  http.begin("https://qianfan.baidubce.com/v2/app/conversation");
  http.addHeader("Content-Type", "application/json");
  http.addHeader("X-Appbuilder-Authorization", "Bearer " + String(api_key));

  // 创建一个 JSON 文档
  DynamicJsonDocument requestJson(1024);
  requestJson["app_id"] = app_id;

  // 将 JSON 数据序列化为字符串
  String requestBody;
  serializeJson(requestJson, requestBody);

  // 发送http访问请求
  int httpCode = http.POST(requestBody);
  if (httpCode == HTTP_CODE_OK) {
    String response = http.getString();
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, response);
    conversation_id = doc["conversation_id"].as<String>();

    // ei_printf("[HTTP] GET conversation_id: %s\n", conversation_id.c_str());
  } else {
    // ei_printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  http.end();

  return conversation_id;
}


// 与百度AppBuiler角色对话
String Conversation_Get(const char* api_key, const char* app_id, const char* conversation_id, String query) {
  String get_conversation_id = "";
  String get_conversation_answer = "";

  // 创建http请求
  HTTPClient http;
  http.begin("https://qianfan.baidubce.com/v2/app/conversation/runs");
  http.addHeader("Content-Type", "application/json");
  http.addHeader("X-Appbuilder-Authorization", "Bearer " + String(api_key));

  // 创建一个 JSON 文档
  DynamicJsonDocument requestJson(1024);
  requestJson["app_id"] = app_id;
  requestJson["query"] = query;
  requestJson["conversation_id"] = conversation_id;
  requestJson["stream"] = false;

  // 将 JSON 数据序列化为字符串
  String requestBody;
  serializeJson(requestJson, requestBody);

  // 发送http访问请求
  int httpCode = http.POST(requestBody);
  if (httpCode == HTTP_CODE_OK) {
    String response = http.getString();
    DynamicJsonDocument doc(4096);
    deserializeJson(doc, response);
    get_conversation_id = doc["conversation_id"].as<String>();
    // ei_printf("[HTTP] GET get_conversation_id: %s\n", get_conversation_id.c_str());
    get_conversation_answer = doc["answer"].as<String>();
    // ei_printf("[HTTP] GET get_conversation_answer: %s\n", get_conversation_answer.c_str());
  } else {
    // ei_printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
  }
  http.end();

  return get_conversation_answer;
}


//对字符串进行URL编码
String urlEncode(const String &str) {
  String encoded = "";
  char c;
  char buf[4];
  for (int i = 0; i < str.length(); i++) {
    c = str.charAt(i);
    if ( ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') ||
         c == '-' || c == '_' || c == '.' || c == '~' ) {
      encoded += c;
    } else {
      sprintf(buf, "%%%02X", (unsigned char)c);
      encoded += buf;
    }
  }
  return encoded;
}

//语音合成
void baiduTTS_Send(String access_token, String text) {
  if (access_token == "") {
    Serial0.println("access_token is null");
    return;
  }

  if (text.length() == 0) {
    Serial0.println("text is null");
    return;
  }

  const int per = PERSON_VOICE;
  const int spd = 6;
  const int pit = 5;
  const int vol = 15;
  const int aue = 6;

  // 进行 URL 编码
  String encodedText = urlEncode(urlEncode(text));

  // URL http请求数据封装
  String url = "https://tsn.baidu.com/text2audio";

  const char* header[] = { "Content-Type", "Content-Length" };

  url += "?tok=" + access_token;
  url += "&tex=" + encodedText;
  url += "&per=" + String(per);
  url += "&spd=" + String(spd);
  url += "&pit=" + String(pit);
  url += "&vol=" + String(vol);
  url += "&aue=" + String(aue);
  url += "&cuid=esp32s3";
  url += "&lan=zh";
  url += "&ctp=1";

  // http请求创建
  HTTPClient http;

  http.begin(url);
  http.collectHeaders(header, 2);

  int httpResponseCode = http.GET();
  if (httpResponseCode > 0) {
    if (httpResponseCode == HTTP_CODE_OK) {
      String contentType = http.header("Content-Type");
      if (contentType.startsWith("audio")) {
        Serial0.println("合成成功");

        uint8_t buffer[512] = { 0 };  // Increased buffer size for smoother playback
        size_t bytesRead = 0;

        int len = http.getSize();  // 读取响应正文数据字节数，如果返回-1是因为响应头中没有Content-Length属性
        // 获取返回的音频数据流
        WiFiClient* stream = http.getStreamPtr();

        while (http.connected() && (len > 0 || len == -1))  // 当前已连接并且有数据可读
        {
          size_t size = stream->available();  // 获取数据流中可用字节数
          if (size) {
            int c = stream->readBytes(buffer, ((size > sizeof(buffer)) ? sizeof(buffer) : size));  // 读取数据到buffer
            playAudio(buffer, c);
            if (len > 0) {
              len -= c;
            }
          }
          delay(1);
        }

        // Flush and stop I2S after all data is processed
        // 清空I2S DMA缓冲区
        delay(800);
        clearAudio();
      } else if (contentType.equals("application/json")) {
        Serial0.println("合成出现错误");
        String response = http.getString();  // Print the error JSON for debugging
        // Serial0.println("%s", response.c_str());

      } else {
        // Serial0.println("未知的Content-Type: %s", contentType);
      }
    } else {
      Serial0.println("Error code: ");
      // Serial0.println("%d", httpResponseCode);
      String response = http.getString();  // Print the error response for debugging
      // Serial0.println("%s", response.c_str());
    }
  } else {
    Serial0.println("Error code: ");
    // Serial0.println("%d", httpResponseCode);
  }
  http.end();
}


bool flag0 = false;
String AccessToken = "";

void http_test(){

// //获取百度访问令牌，一个月执行一次，否则访问百度会失效
// getAccessToken(baidu_api_key, baidu_secret_key);


if(flag0 == false)
{
    flag0 = true;
    //从SD卡读取令牌
    AccessToken = readFileToString(SD, "/Access_token.txt"); 
    //获取对话ID
    appbuilder_conversation_id = getConversation_id(appbuilder_api_key, appbuilder_app_id);
}

//语音识别麦克风
    String recognizedText = baiduSTT_Send(AccessToken, AuDio_data, AuDio_len);
    printfme("问：%s\n",recognizedText.c_str());


//与模型对话
    // String recognizedText = "我要学英语";
    String response_answer = Conversation_Get(appbuilder_api_key, appbuilder_app_id, appbuilder_conversation_id.c_str(), recognizedText);
    printfme("答：");
    Serial0.println(response_answer.c_str());


//语音播放
    // read_wav(SD, "/test.wav");
    // I2S_Write(pcmData, pcmDataSize);
    // printfme("%d\n",pcmDataSize);

//语音合成与播放
    // String AccessToken = readFileToString(SD, "/Access_token.txt"); 
    Logo_display(speak_logo,WHITE,BLACK);
    baiduTTS_Send(AccessToken, response_answer);
    Logo_display(normal_logo,WHITE,BLACK);


}





